<?php
namespace Common\Common;
/**
 * HttpClient 基于curl实现
 * @author @Andre余猛 <andre@intlime.com>
 * @version 0.2.3
 * @time 2014.04.15
 */
class HttpClient{
	// Request vars
	private $url = null;
	// var $host;
	// var $port="80";
	// var $path;
	private $method;
	private $postdata = null;
	private $cookies = array();
	private $referer;
	//var $accept = 'text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,image/jpeg,image/gif,*/*';
	//var $accept_encoding = 'gzip';
	//var $accept_language = 'en-us';
	private $user_agent = 'HttpClient by Andre v0.2';
	
	// Options
	private $debug = false;
	private $timeout = 40;
	private $use_gzip = true;
	private $persist_cookies = true;  // If true, received cookies are placed in the $this->cookies array ready for the next request
	// Note: This currently ignores the cookie path (and time) completely. Time is not important,
	//       but path could possibly lead to security problems.
	//var $persist_referers = true; // For each request, sends path of last request as referer
	//var $debug = false;
	private $handle_redirects = false; // Auaomtically redirect if Location or URI header is found
	private $max_redirects = 5;
	private $headers_only = false;    // If true, stops receiving once headers have been read.
	
	// Basic authorization variables
	//var $username;
	//var $password;
	
	// Response vars  
	private $result;
	private $status;
	private $headers = array();
	private $content = null;
	private $errormsg;
	
	// Tracker variables
	//var $redirect_count = 0;
	//var $cookie_host = '';
	
	//SSL和CA验证
	private $ca = false;
	private $ssl = false;
	private $cacert =null;
	
	public function __construct($url = null) {
		$this->url = $url;
    }
    
    public function get($url = null, $data = null) {
		if ($url) {
			$this->url = $url;
		}
        $this->method = 'GET';
        if ($data) {
    		$parese_url = parse_url($this->url);
    		if (isset($parese_url['query']) && $parese_url['query']) {
    			$this->url .= '&'.$this->buildQueryString($data);
    		} else {
    			$this->url .= '?'.$this->buildQueryString($data);
    		}		
        }

        return $this->doRequest();
    }
    
    public function post($url = null, $data = null) {
    	if ($url) {
			$this->url = $url;
		}
        $this->method = 'POST';
        $this->postdata = $this->buildQueryString($data);
        //自定义头
        if(!empty($this->headers)){
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_HTTPHEADERS,$this->headers);
        }
    	return $this->doRequest();
    }
    
    private function buildQueryString($data) {
    	$querystring = '';
    	if (is_array($data)) {
    		// Change data in to postable data
    		foreach ($data as $key => $val) {
    			if (is_array($val)) {
    				foreach ($val as $val2) {
    					$querystring .= urlencode($key).'='.urlencode($val2).'&';
    				}
    			} else {
    				$querystring .= urlencode($key).'='.urlencode($val).'&';
    			}
    		}
    		$querystring = substr($querystring, 0, -1);
    	} else {
    		$querystring = $data;
    	}
    	return $querystring;
    }
    
	private function doRequest()
	{
	    
		if (!$this->url) {
			throw new \Exception("url is null", 1);
		}
		//SSL链接校验
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_URL, $this->url);
		$this->ssl = substr($this->url, 0, 8) == "https://" ? true : false;//检测是否为https链接
		if ($this->ssl  && $this->ca) {
		    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);   // 只信任CA颁布的证书
		    curl_setopt($ch, CURLOPT_CAINFO, $this->cacert); // CA根证书（用来验证的网站证书是否是CA颁布）
		    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); // 检查证书中是否设置域名，并且是否与提供的主机名匹配
		} else if ($this->ssl  && !$this->ca) {
		    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 信任任何证书
		    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1); // 检查证书中是否设置域名
		}
		// curl_setopt($ch, CURLOPT_PORT, $this->port);
		if($this->referer){
			curl_setopt($ch,CURLOPT_REFERER,$this->referer);
		}
		//curl_setopt($ch, CURLOPT_AUTOREFERER, true);
		if($this->use_gzip){
			curl_setopt($ch, CURLOPT_ENCODING, 'gzip');
		}
		if($this->method == 'POST'){
			//发送一个常规的POST请求，类型为：application/x-www-form-urlencoded
			curl_setopt($ch, CURLOPT_POST, true) ;
		}
		if($this->postdata){
			curl_setopt($ch,CURLOPT_POSTFIELDS,$this->postdata);
		}
		curl_setopt($ch,CURLOPT_USERAGENT,$this->user_agent);
		//设置连接等待时间,0不等待
		curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->timeout);
		//设置curl允许执行的最长秒数
		curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
		//启用时会将服务器服务器返回的“Location:”放在header中递归的返回给服务器
		curl_setopt($ch, CURLOPT_FOLLOWLOCATION, $this->handle_redirects);
		
// 		curl_setopt($ch, CURLOPT_MAXREDIRS, $this->max_redirects);
		if ($this->cookies) {
			$cookie = null;
			foreach ($this->cookies as $key => $value) {
				$cookie .= "$key=$value; ";
			}
			curl_setopt( $ch, CURLOPT_COOKIE, $cookie);
		}
// 		$this->debug($cookie);
		curl_setopt($ch, CURLOPT_NOBODY, $this->headers_only);
		//是否将头文件的信息作为数据流输出(HEADER信息),这里保留报文
		curl_setopt($ch, CURLOPT_HEADER, true);
		//获取的信息以文件流的形式返回，而不是直接输出。
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true) ;
		curl_setopt($ch, CURLOPT_BINARYTRANSFER, true) ;
		
		$result = curl_exec($ch);
		$this->status = curl_getinfo($ch,CURLINFO_HTTP_CODE);
		if(curl_errno($ch)){//出错则抛出错误信息
			$_erroStr = curl_error($ch);
			$_erroNo = curl_errno($ch);
			$this->errormsg = $_erroStr;
			return false;
		}
		curl_close($ch); //关闭curl链接
		
		//对HTTP 100 continue做兼容
		$temp=explode("\r\n", $result,2);
		if ('HTTP/1.1 100 Continue' == $temp[0]) {
			$result=explode("\r\n\r\n", $result,2);
			$result=$result[1];	
		}
		$this->result=$result;
		$result=explode("\r\n\r\n", $result,2);
		$this->content=$result[1];
		$this->headers = array();
		$headers=$result[0];
		$this->debug($headers);
		$headers=explode("\r\n", $headers);
		foreach ($headers as $line){
			if (!preg_match('/([^:]+):\\s*(.*)/', $line, $m)) {
				// Skip to the next header
				continue;
			}
// 			$this->debug(trim($line));
			$key = strtolower(trim($m[1]));
			$val = trim($m[2]);
			// Deal with the possibility of multiple headers of same name
			if (isset($this->headers[$key])) {
				if (is_array($this->headers[$key])) {
					$this->headers[$key][] = $val;
				} else {
					$this->headers[$key] = array($this->headers[$key], $val);
				}
			} else {
				$this->headers[$key] = $val;
			}
		}
		
		if ($this->persist_cookies && isset($this->headers['set-cookie'])) {
			$cookies = $this->headers['set-cookie'];
			if (!is_array($cookies)) {
				$cookies = array($cookies);
			}
			foreach ($cookies as $cookie) {
				if (preg_match('/([^=]+)=([^;]+);/', $cookie, $m)) {
					$this->cookies[$m[1]] = $m[2];
				}
			}
		}
		return true;
	}
	
	public function getResult() {
		return $this->result;
	}
	public function getStatus() {
		return $this->status;
	}
	public function getContent() {
		return $this->content;
	}
	public function getHeaders() {
		return $this->headers;
	}
	public function getHeader($header) {
		$header = strtolower($header);
		if (isset($this->headers[$header])) {
			return $this->headers[$header];
		} else {
			return false;
		}
	}
	public function getError() {
		return $this->errormsg;
	}
	public function getCookies() {
		return $this->cookies;
	}
	public function getRequestURL() {
		return $this->url;
	}
	// Setter methods
	public function setUserAgent($string) {
		$this->user_agent = $string;
	}
	/*
	function setAuthorization($username, $password) {
		$this->username = $username;
		$this->password = $password;
	}*/
	public function setCookies($array) {
		$this->cookies = $array;
	}
	// Option setting methods
	public function setUseGzip($boolean) {
		$this->use_gzip = $boolean;
	}
	public function setPersistCookies($boolean) {
		$this->persist_cookies = $boolean;
	}
	public function setReferer($referer) {
		$this->referer = $referer;
	}
	public function setPersistReferers($boolean) {
		$this->persist_referers = $boolean;
	}
	public function setHandleRedirects($boolean) {
		$this->handle_redirects = $boolean;
	}
	public function setMaxRedirects($num) {
		$this->max_redirects = $num;
	}
	public function setHeadersOnly($boolean) {
		$this->headers_only = $boolean;
	}
	public function setDebug($boolean) {
		$this->debug = $boolean;
	}
    public function setCacertPath($str){
        $this->ca = true;
        $this->cacert = $str;
    }
    public function sethttpHeader($arr){
        $this->headers=$arr;
    }
    
	public static function quickGet($url, $data = null) {
		$client = new HttpClient();
		if (!$client->get($url, $data)) {
			return false;
		} else {
			return $client->getContent();
		}
	}
	
	public static function quickPost($url, $data = null) {
		$client = new HttpClient($url);
		if (!$client->post($url, $data)) {
			return false;
		} else {
			return $client->getContent();
		}
	}
	
	private function debug($msg, $object = false) {
		if ($this->debug) {
			print '<div style="border: 1px solid red; padding: 0.5em; margin: 0.5em;"><strong>HttpClient Debug:</strong> '.$msg;
			if ($object) {
				ob_start();
				print_r($object);
				$content = htmlentities(ob_get_contents());
				ob_end_clean();
				print '<pre>'.$content.'</pre>';
			}
			print '</div>';
		}
	}
}
